
死锁(Deadlock)的简明定义
死锁是指多个进程(或线程、协程)在竞争资源时,因互相持有对方所需的资源且不释放,导致所有参与者无限阻塞的状态。
死锁的四个必要条件
-
互斥条件:资源一次只能被一个进程占用(如锁、文件句柄)。
-
占有并等待:进程持有至少一个资源,同时等待其他被占用的资源。
-
非抢占条件:已分配给进程的资源不能被强制剥夺,只能主动释放。
-
循环等待:多个进程形成环形依赖链(如A等B,B等C,C等A)。
👉 只有这四个条件同时满足时,死锁才会发生。
Go语言中的死锁示例
1,显式死锁直接崩溃
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select (no cases)]:
main.main()
/home/hezichao/go_project/test/go_test/prop/deadlock/deadlock.go:29 +0x2b
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
var mu1, mu2 sync.Mutex func main() { go func() { mu1.Lock() // 协程1持有mu1 defer mu1.Unlock() time.Sleep(time.Second * 10) // 模拟耗时操作 mu2.Lock() // 协程1尝试获取mu2(但mu2被协程2持有) defer mu2.Unlock() }() go func() { mu2.Lock() // 协程2持有mu2 defer mu2.Unlock() mu1.Lock() // 协程2尝试获取mu1(死锁发生!) defer mu1.Unlock() }() select {} // 阻塞主线程 } |
2,真实场景示例:局部死锁导致性能下降(不崩溃)(代码暂未模拟出)
场景描述
假设有一个 电商订单系统,用户支付成功后,系统需要:
-
扣减库存(inventory_service)。
-
生成物流单(shipping_service)。
-
更新订单状态(order_service)。
由于服务间存在依赖关系,高并发时可能发生局部死锁,表现为:
如何避免死锁?
-
破坏占有并等待:一次性申请所有所需资源(如全局锁)。
-
破坏非抢占:使用超时机制(如TryLock或context.WithTimeout)。
-
破坏循环等待:统一资源获取顺序(如总是先锁A再锁B)。
-
工具检测:
死锁是多个执行单元因资源竞争陷入的永久阻塞状态,需通过设计规范或工具预防。
-
区别:
- 死锁 是一种特定的阻塞状态,通常涉及多个 goroutine 和锁的相互等待。
- goroutine 阻塞 是一个更广泛的概念,指任何导致 goroutine 无法继续执行的状态。
- channel 阻塞 是 goroutine 阻塞的一种特定情况,特指在使用 channel 进行通信时的阻塞。
-
联系:
- 死锁是 goroutine 阻塞的一种形式,但并不是所有的 goroutine 阻塞都是死锁。
- channel 阻塞是 goroutine 阻塞的一个常见场景,但它可以是正常的、预期的行为,而非错误。
「三年博客,如果觉得我的文章对您有用,请帮助本站成长」
共有 0 - 使用 pprof 排查 Go 死锁的步骤